#include "operation.h"
#include <cmath>

Operation::Operation()
{
}

Constante* Addition::apply(const Constante& c1, const Constante& c2){

        Constante* result;
        if(c1.getType() > c2.getType()){
                result = c1+c2;
        }
        else{
                result = c2+c1;
        }
        return result;

}

Constante* Substartion::apply(const Constante& c1, const Constante& c2){

       Constante* result;
        if(c1.getType() > c2.getType()){
                result = c1-c2;
        }
        else{
                result = c2-c1;
        }
        return result;

}

Constante* Multiplcation::apply(const Constante& c1, const Constante& c2){

       Constante* result;
        if(c1.getType() > c2.getType()){
                result = c1*c2;
        }
        else{
                result = c2*c1;
        }
        return result;

}

Constante* Division::apply(const Constante& c1, const Constante& c2){

       Constante* result;
        if(c1.getType() == Constante::INTEGER && c2.getType() == Constante::INTEGER){
                result = new Fraction(static_cast<const Integer&>(c2),static_cast<const Integer&>(c1));
        }
        else if(c1.getType() >= c2.getType()){
                result = c1/c2;
        }
        else{
                result = c2/c1;
        }
        return result;

}

Constante* Pow::apply(const Constante& c1, const Constante& c2){

       Constante* result = new Integer(1);
       if(c2.getType() != Constante::INTEGER)
           ;//erreur
       for(int i=0;i<static_cast<const Integer&>(c2).getVal();i++)
            result = (*result) *c1;
       return result;

}

Constante* Sign::apply(const Constante& c1){
       return -c1;
}

Constante* Square::apply(const Constante& c1){
       return c1*c1;
}

Constante* Cube::apply(const Constante& c1){
       return Pow::apply(c1,Integer(3));
}

Constante* Inv::apply(const Constante& c1){
       Constante* result;
        switch(c1.getType()){
                case Constante::INTEGER:
        result = new Fraction(1,static_cast<const Integer&>(c1).getVal());
                break;
                case Constante::RATIONNAL:
        result = Fraction(1) / c1;
                break;
                case Constante::REAL:
        result = new Fraction(1,(int)static_cast<const Real&>(c1).getVal());
                break;
        }
       return result;
}

Constante* Sqrt::apply(const Constante& c1){
       Constante* result;
        switch(c1.getType()){
                case Constante::INTEGER:
        result = new Real(sqrt(static_cast<const Integer&>(c1).getVal()));
                break;
                case Constante::RATIONNAL:
        result = new Real(sqrt(static_cast<const Fraction&>(c1).getNum())/sqrt(static_cast<const Fraction&>(c1).getNum()));
                break;
                case Constante::REAL:
        result = new Real(sqrt(static_cast<const Real&>(c1).getVal()));
                break;
        }
       return result;
}

Constante* Sin::apply(const Constante& c1){
       Constante* result;
        switch(c1.getType()){
                case Constante::INTEGER:
        result = new Real(sin(static_cast<const Integer&>(c1).getVal()));
                break;
                case Constante::RATIONNAL:
        result = new Real(sin(static_cast<const Fraction&>(c1).getNum())/sqrt(static_cast<const Fraction&>(c1).getNum()));
                break;
                case Constante::REAL:
        result = new Real(sin(static_cast<const Real&>(c1).getVal()));
                break;
        }
       return result;
}
Constante* Sinh::apply(const Constante& c1){
       Constante* result;
        switch(c1.getType()){
                case Constante::INTEGER:
        result = new Real(sinh(static_cast<const Integer&>(c1).getVal()));
                break;
                case Constante::RATIONNAL:
        result = new Real(sinh(static_cast<const Fraction&>(c1).getNum())/sqrt(static_cast<const Fraction&>(c1).getNum()));
                break;
                case Constante::REAL:
        result = new Real(sinh(static_cast<const Real&>(c1).getVal()));
                break;
        }
       return result;
}
Constante* Cos::apply(const Constante& c1){
       Constante* result;
        switch(c1.getType()){
                case Constante::INTEGER:
        result = new Real(cos(static_cast<const Integer&>(c1).getVal()));
                break;
                case Constante::RATIONNAL:
        result = new Real(cos(static_cast<const Fraction&>(c1).getNum())/sqrt(static_cast<const Fraction&>(c1).getNum()));
                break;
                case Constante::REAL:
        result = new Real(cos(static_cast<const Real&>(c1).getVal()));
                break;
        }
       return result;
}
Constante* Cosh::apply(const Constante& c1){
       Constante* result;
        switch(c1.getType()){
                case Constante::INTEGER:
        result = new Real(cosh(static_cast<const Integer&>(c1).getVal()));
                break;
                case Constante::RATIONNAL:
        result = new Real(cosh(static_cast<const Fraction&>(c1).getNum())/sqrt(static_cast<const Fraction&>(c1).getNum()));
                break;
                case Constante::REAL:
        result = new Real(cosh(static_cast<const Real&>(c1).getVal()));
                break;
        }
       return result;
}
Constante* Tan::apply(const Constante& c1){
       Constante* result;
        switch(c1.getType()){
                case Constante::INTEGER:
        result = new Real(tan(static_cast<const Integer&>(c1).getVal()));
                break;
                case Constante::RATIONNAL:
        result = new Real(tan(static_cast<const Fraction&>(c1).getNum())/sqrt(static_cast<const Fraction&>(c1).getNum()));
                break;
                case Constante::REAL:
        result = new Real(tan(static_cast<const Real&>(c1).getVal()));
                break;
        }
       return result;
}
Constante* Tanh::apply(const Constante& c1){
       Constante* result;
        switch(c1.getType()){
                case Constante::INTEGER:
        result = new Real(tanh(static_cast<const Integer&>(c1).getVal()));
                break;
                case Constante::RATIONNAL:
        result = new Real(tanh(static_cast<const Fraction&>(c1).getNum())/sqrt(static_cast<const Fraction&>(c1).getNum()));
                break;
                case Constante::REAL:
        result = new Real(tanh(static_cast<const Real&>(c1).getVal()));
                break;
        }
       return result;
}

Constante* Log::apply(const Constante& c1){
       Constante* result;
        switch(c1.getType()){
                case Constante::INTEGER:
        result = new Real(log10(static_cast<const Integer&>(c1).getVal()));
                break;
                case Constante::RATIONNAL:
        result = new Real(log10(static_cast<const Fraction&>(c1).getNum())/sqrt(static_cast<const Fraction&>(c1).getNum()));
                break;
                case Constante::REAL:
        result = new Real(log10(static_cast<const Real&>(c1).getVal()));
                break;
        }
       return result;
}
Constante* Ln::apply(const Constante& c1){
       Constante* result;
        switch(c1.getType()){
                case Constante::INTEGER:
        result = new Real(log(static_cast<const Integer&>(c1).getVal()));
                break;
                case Constante::RATIONNAL:
        result = new Real(log(static_cast<const Fraction&>(c1).getNum())/sqrt(static_cast<const Fraction&>(c1).getNum()));
                break;
                case Constante::REAL:
        result = new Real(log(static_cast<const Real&>(c1).getVal()));
                break;
        }
       return result;
}
